package com.sinosoft.cpyy.util.number;

import com.sinosoft.cpyy.model.constant.BaseDictEnum;
import com.sinosoft.util.NumberUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.math.BigDecimal;

/**
 * This class is converts a Double to a Round number,
 * Registered for use in .enjoy it.
 * <p/>精度规则处理引擎</p>
 */
@Slf4j
public class CalcEngine {

    /**
     * Date 2017/12/27
     * Author lihao [lihao@sinosoft.com.cn]
     * 方法说明: 计算主方法
     * 计算原则:
     * 1.参与计算:先计算并取上下限,再取精度并再次取上下限
     * 2.不参与计算:先取精度,再取上下限
     * 3.先舍入,再取奇偶
     *
     * @param pc 计算规则类
     */
    public static Double calc(PrecisionCounter pc) {
        Double value = null;
        //是否计算
        if (null != pc.getIsPart() && pc.getIsPart()) {
            value = pc.getProtoValue();
        } else {
            value = calcMin(pc).doubleValue();
        }

        if (null == value) {
            value = 0.0;
        }

        try {
            value = upDown(pc, new BigDecimal(value.toString()));
            if (null != pc.getIsPart() && pc.getIsPart()) {
                PrecisionCounter target = new PrecisionCounter();
                BeanUtils.copyProperties(pc, target);
                target.setIsPart(false);
                target.setProtoValue(value);
                value = calcMin(target).doubleValue();
                value = upDown(target, new BigDecimal(value.toString()));
            }

        } catch (Exception e) {
            log.error("精度规则处理引擎异常:{}", e);
        }
        return value;
    }


    //计算上下限
    public static Double upDown(PrecisionCounter pc, BigDecimal fo) throws Exception {
        if (pc != null && fo != null) {
            BigDecimal f = fo;
            //是否通过比例,附加计算
            if (pc.getIsPart()) {
                f = partAppe(pc, fo);
            }
            if (f == BigDecimal.ZERO) {
                return f.doubleValue();
            }
            //小于下限取下限
            if (null != pc.getDownValue() && (f.subtract(new BigDecimal(pc.getDownValue())).compareTo(BigDecimal.ZERO) == -1)) {
                return pc.getDownValue();
            }
            //高于上限取上限
            if (null != pc.getUpValue() && (f.subtract(new BigDecimal(pc.getUpValue())).compareTo(BigDecimal.ZERO) == 1)) {
                return pc.getUpValue();
            }
            return f.doubleValue();
        } else {
            throw new Exception("传递参数有错误！");
        }

    }

    //计算比例和附加
    public static BigDecimal partAppe(PrecisionCounter pc, BigDecimal f) {
        // 是否有比例
        if (null != pc.getPersPart()) {
            f = f.multiply(pc.getPersPart());
        } else {
            f = new BigDecimal(0);
        }
        //是否有附加
        if (null != pc.getPersAppe())
            f = f.add(new BigDecimal(pc.getPersAppe().toString()));
        return f;
    }


    //根据精度计算金额
    public static BigDecimal calcMin(PrecisionCounter pc) {
        try {
            /**
             * 精度:
             * 厘位：3 ;
             * 分位：2 ;
             * 角位：1 ;
             * 原始返回 : 0
             * 元位：-10 ;
             * 十元位：-20;
             * 百位：-30
             */
            //精度默认值
            Integer i = 0;
            //四舍五入默认值
            Integer rounWay = 0;
            //默认奇偶数
            Integer isparity = 1;

            //原始值不为空
            if (pc.getProtoValue() != null) {
                Double protoValue = pc.getProtoValue();
                //精度不为空
                if (pc.getPrec() != null) {
                    i = pc.getPrec();
                } else {
                    i = 2;// 默认精度分
                }
                //舍入原则不为空
                if (pc.getRounWay() != null) {
                    rounWay = pc.getRounWay();
                } else {
                    rounWay = 1;// 默认四舍五入
                }
                //基偶原则不为空
                if (pc.getParity() != null) {
                    isparity = pc.getParity();
                } else {
                    isparity = 1;// 默认奇偶数
                }

                switch (i) {// 精度
                    case 3:
                        if (parity(makeNumber3(protoValue, rounWay), BaseDictEnum.PROD_PEACCU_3.getValue()) || isparity == 1) {
                            return makeNumber3(makeNumber3(protoValue, rounWay).doubleValue(), rounWay);
                        }
                        if (isparity == 2) {
                            return makeNumber3(makeNumber3(protoValue, rounWay).doubleValue() + 0.001D, rounWay);
                        }
                        if (isparity == 3) {
                            return makeNumber3(makeNumber3(protoValue, rounWay).doubleValue() - 0.001D, rounWay);
                        }
                        if (isparity == 4) {
                            return makeNumber3(makeNumber3(protoValue, rounWay).doubleValue() + 0.002D, rounWay);
                        }
                        if (isparity == 5) {
                            return makeNumber3(makeNumber3(protoValue, rounWay).doubleValue() - 0.002D, rounWay);
                        }
                        break;
                    case 2:
                        if (parity(makeNumber2(protoValue, rounWay), BaseDictEnum.PROD_PEACCU_2.getValue()) || isparity == 1)
                            return makeNumber2(protoValue, rounWay);
                        if (isparity == 2) {
                            return makeNumber2(makeNumber3(protoValue, rounWay).doubleValue() + 0.01D, rounWay);
                        }
                        if (isparity == 3) {
                            return makeNumber2(makeNumber3(protoValue, rounWay).doubleValue() - 0.01D, rounWay);
                        }
                        if (isparity == 4) {
                            return makeNumber2(makeNumber3(protoValue, rounWay).doubleValue() + 0.02D, rounWay);
                        }
                        if (isparity == 5) {
                            return makeNumber2(makeNumber3(protoValue, rounWay).doubleValue() - 0.02D, rounWay);
                        }
                        break;
                    case 1:
                        if (parity(makeNumber1(protoValue, rounWay), BaseDictEnum.PROD_PEACCU_1.getValue()) || isparity == 1)
                            return makeNumber1(protoValue, rounWay);
                        if (isparity == 2) {
                            return makeNumber1(makeNumber3(protoValue, rounWay).doubleValue() + 0.1D, rounWay);
                        }
                        if (isparity == 3) {
                            return makeNumber1(makeNumber3(protoValue, rounWay).doubleValue() - 0.1D, rounWay);
                        }
                        if (isparity == 4) {
                            return makeNumber1(makeNumber3(protoValue, rounWay).doubleValue() + 0.2D, rounWay);
                        }
                        if (isparity == 5) {
                            return makeNumber1(makeNumber3(protoValue, rounWay).doubleValue() - 0.2D, rounWay);
                        }
                        break;
                    case 0:
                        return makeNumber0(protoValue);
                    case -10:
                        if (parity(makeNumberN1(protoValue, rounWay), BaseDictEnum.PROD_PEACCU_4.getValue()) || isparity == 1)
                            return makeNumberN1(protoValue, rounWay);
                        if (isparity == 2) {
                            return makeNumberN1(makeNumber3(protoValue, rounWay).doubleValue() + 1.0D, rounWay);
                        }
                        if (isparity == 3) {
                            return makeNumberN1(makeNumber3(protoValue, rounWay).doubleValue() - 1.0D, rounWay);
                        }
                        if (isparity == 4) {
                            return makeNumberN1(makeNumber3(protoValue, rounWay).doubleValue() + 2.0D, rounWay);
                        }
                        if (isparity == 5) {
                            return makeNumberN1(makeNumber3(protoValue, rounWay).doubleValue() - 2.0D, rounWay);
                        }
                        break;
                    case -20:
                        if (parity(makeNumberN2(protoValue, rounWay), BaseDictEnum.PROD_PEACCU_5.getValue()) || isparity == 1)
                            return makeNumberN2(protoValue, rounWay);
                        if (isparity == 2) {
                            return makeNumberN2(makeNumber3(protoValue, rounWay).doubleValue() + 10.0D, rounWay);
                        }
                        if (isparity == 3) {
                            return makeNumberN2(makeNumber3(protoValue, rounWay).doubleValue() - 10.0D, rounWay);
                        }
                        if (isparity == 4) {
                            return makeNumberN2(makeNumber3(protoValue, rounWay).doubleValue() + 20.0D, rounWay);
                        }
                        if (isparity == 5) {
                            return makeNumberN2(makeNumber3(protoValue, rounWay).doubleValue() - 20.0D, rounWay);
                        }
                        break;
                    case -30:
                        if (parity(makeNumberN3(protoValue, rounWay), BaseDictEnum.PROD_PEACCU_6.getValue()) || isparity == 1)
                            return makeNumberN3(protoValue, rounWay);
                        if (isparity == 2) {
                            return makeNumberN3(makeNumber3(protoValue, rounWay).doubleValue() + 100.0D, rounWay);
                        }
                        if (isparity == 3) {
                            return makeNumberN3(makeNumber3(protoValue, rounWay).doubleValue() - 100.0D, rounWay);
                        }
                        if (isparity == 4) {
                            return makeNumberN3(makeNumber3(protoValue, rounWay).doubleValue() + 200.0D, rounWay);
                        }
                        if (isparity == 5) {
                            return makeNumberN3(makeNumber3(protoValue, rounWay).doubleValue() - 200.0D, rounWay);
                        }
                        break;
                    default:
                        //没有匹配到对应的精度,返回原始值
                        return makeNumber0(protoValue);
                }
            }
        } catch (Exception e) {
            log.error("精度规则处理引擎异常:{}", e);
            //TODO 后期抛出指定异常
        }
        return BigDecimal.valueOf(0);
    }

    /**
     * 舍入原则   :
     * 四舍五入:   1 ;
     * 截断舍去:   2 ;
     * 进位:       3 ;
     * 见分进元:   4 ;
     * 见分进十元: 5 ;
     */
    public static BigDecimal makeNumber3(Double f, Integer rounWay) {
        // TODO:厘位规则0.001
        switch (rounWay) {
            //.5进位
            case 1:
                return RoundTool.round(f, 3, BigDecimal.ROUND_HALF_UP);
            case 2:
                return RoundTool.round(f, 3, BigDecimal.ROUND_DOWN);
            case 3:
                return RoundTool.round(turnNumber(f, 3), 3, BigDecimal.ROUND_UP);
            case 4:
                return RoundTool.round(crossMakeNumber(f, 3), 3, BigDecimal.ROUND_HALF_DOWN);
            default:
                return null;
        }
    }

    /**
     * Date 2017/11/17
     * Author lihao [lihao@sinosoft.com.cn]
     * 方法说明: 根据舍入原则计算 分位规则(保留两位小数)
     *
     * @param f       数据
     * @param rounWay 舍入原则
     */
    public static BigDecimal makeNumber2(Double f, Integer rounWay) {
        // TODO:分位规则0.01
        switch (rounWay) {
            //.5进位
            case 1:
                return RoundTool.round(f, 2, BigDecimal.ROUND_HALF_UP);
            case 2:
                return RoundTool.round(f, 2, BigDecimal.ROUND_DOWN);
            case 3:
                return RoundTool.round(turnNumber(f, 2), 2, BigDecimal.ROUND_UP);
            case 4:
                BigDecimal db = RoundTool.round(crossMakeNumber(f, 2), 2, BigDecimal.ROUND_HALF_DOWN);
                return db;
            default:
                return null;
        }

    }

    /**
     * Date 2017/11/17
     * Author lihao [lihao@sinosoft.com.cn]
     * 方法说明: 根据舍入原则计算 角位规则(保留一位小数)
     *
     * @param f       数据
     * @param rounWay 舍入原则
     */
    public static BigDecimal makeNumber1(Double f, Integer rounWay) {
        // TODO:角位规则0.1
        switch (rounWay) {
            //.5进位
            case 1:
                return RoundTool.round(f, 1, BigDecimal.ROUND_HALF_UP);
            case 2:
                return RoundTool.round(f, 1, BigDecimal.ROUND_DOWN);
            case 3:
                return RoundTool.round(turnNumber(f, 1), 1, BigDecimal.ROUND_UP);
            case 4:
                return RoundTool.round(crossMakeNumber(f, 1), 1, BigDecimal.ROUND_HALF_DOWN);
            default:
                return null;
        }

    }

    /**
     * Date 2017/11/17
     * Author lihao [lihao@sinosoft.com.cn]
     * 方法说明: 将 Double 类型的数据转换为 BigDecimal 类型数据,并返回
     *
     * @param f 数据
     */
    public static BigDecimal makeNumber0(Double f) {
        // TODO:返回原始值
        return BigDecimal.valueOf(f);

    }

    /**
     * Date 2017/11/17
     * Author lihao [lihao@sinosoft.com.cn]
     * 方法说明: 根据舍入原则计算 元位规则(没有小数)
     *
     * @param f       数据
     * @param rounWay 舍入原则
     */
    public static BigDecimal makeNumberN1(Double f, Integer rounWay) {
        // TODO:元位规则1.0
        switch (rounWay) {
            //.5 进位
            case 1:
                return RoundTool.round(f, 0, BigDecimal.ROUND_HALF_UP);
            case 2:
                return RoundTool.round(f, 0, BigDecimal.ROUND_DOWN);
            case 3:
                return RoundTool.round(turnNumber(f, 0), 0, BigDecimal.ROUND_UP);
            case 4:
                return RoundTool.round(crossMakeNumber(f, 0), 0, BigDecimal.ROUND_HALF_DOWN);
            default:
                return null;
        }

    }

    public static BigDecimal makeNumberN2(Double f, Integer rounWay) {
        // TODO:十位规则10.0
        switch (rounWay) {
            //.5进位
            case 1:
                return RoundTool.round(f, -1, BigDecimal.ROUND_HALF_UP);
            case 2:
                return RoundTool.round(f, -1, BigDecimal.ROUND_DOWN);
            case 3:
                return RoundTool.round(turnNumber(f, -1), -1, BigDecimal.ROUND_UP);
            case 4:
                return RoundTool.round(crossMakeNumber(f, -1), -1, BigDecimal.ROUND_HALF_DOWN);
            default:
                return null;
        }

    }

    public static BigDecimal makeNumberN3(Double f, Integer rounWay) {
        // TODO:百位规则100.0
        switch (rounWay) {
            //.5进位
            case 1:
                return RoundTool.round(f, -2, BigDecimal.ROUND_HALF_UP);
            case 2:
                return RoundTool.round(f, -2, BigDecimal.ROUND_DOWN);
            case 3:
                return RoundTool.round(turnNumber(f, -2), -2, BigDecimal.ROUND_UP);
            case 4:
                return RoundTool.round(crossMakeNumber(f, -2), -2, BigDecimal.ROUND_HALF_DOWN);
            default:
                return null;
        }

    }

    /**
     * 跨位进方法，精度之后第一位、或第二位不为零，则进位。
     * <p>
     * 精度  rounWay:
     * 厘位：3 ;
     * 分位：2 ;
     * 角位：1 ;
     * 元位 : 0
     * 十元位：-1;
     * 百位：-2
     */
    public static BigDecimal crossMakeNumber(Double value, int rounWay) {
        try {
            BigDecimal cot = new BigDecimal(0);
            BigDecimal decimal = new BigDecimal(value.toString()).setScale(rounWay + 2, BigDecimal.ROUND_DOWN);
            String decimalStr = String.valueOf(decimal).replace(".", "");
            if (decimalStr.substring(decimalStr.length() - 2).equals("00"))
                return decimal;
            else {
                String retVal = "";
                switch (rounWay) {
                    case 3:
                        cot = new BigDecimal(0.001);
                        retVal = String.valueOf(decimal);
                        return cot.add(new BigDecimal(retVal.substring(0, retVal.length() - 2)));
                    case 2:
                        cot = new BigDecimal(0.01);
                        retVal = String.valueOf(decimal);
                        return cot.add(new BigDecimal(retVal.substring(0, retVal.length() - 2)));
                    case 1:
                        cot = new BigDecimal(0.1);
                        retVal = String.valueOf(decimal);
                        return cot.add(new BigDecimal(retVal.substring(0, retVal.length() - 2)));
                    case 0:
                        cot = new BigDecimal(1);
                        retVal = String.valueOf(decimal);
                        return cot.add(new BigDecimal(retVal.substring(0, retVal.length() - 3)));
                    case -1:
                        cot = new BigDecimal(10);
                        retVal = String.valueOf(decimal);
                        return cot.add(new BigDecimal(retVal.substring(0, retVal.length() - 2)));
                    case -2:
                        cot = new BigDecimal(100);
                        retVal = String.valueOf(decimal);
                        return cot.add(new BigDecimal(retVal));
                }
                return decimal;
            }
        } catch (Exception e) {
            return new BigDecimal(value.toString()).setScale(rounWay + 2, BigDecimal.ROUND_HALF_UP);
        }
    }

    /**
     * 进位处理 精度之后一位不为零则进位
     *
     * @param value
     * @return
     * @方法说明
     * @date 2015-3-27
     * @author 梅海波
     */
    public static Double turnNumber(Double value, int rounWay) {
        BigDecimal decimal = null;
        String doubleStr = null;
        switch (rounWay) {
            case -1:
                decimal = new BigDecimal(value.toString()).setScale(rounWay + 2, BigDecimal.ROUND_DOWN);
                doubleStr = String.valueOf(decimal);
                if (doubleStr.substring(0, doubleStr.length() - 2).charAt(doubleStr.substring(0, doubleStr.length() - 2).length() - 1) == '0') {
                    return new BigDecimal(value.toString()).setScale(0, BigDecimal.ROUND_DOWN).doubleValue();
                } else {
                    return value;
                }
            case -2:
                decimal = new BigDecimal(value.toString()).setScale(rounWay + 3, BigDecimal.ROUND_DOWN);
                doubleStr = String.valueOf(decimal);
                if (doubleStr.substring(0, doubleStr.length() - 2).charAt(doubleStr.substring(0, doubleStr.length() - 2).length() - 2) == '0') {
                    return new BigDecimal(value.toString()).setScale(-1, BigDecimal.ROUND_DOWN).doubleValue();
                } else {
                    return value;
                }
            default:
                decimal = new BigDecimal(value.toString()).setScale(rounWay + 1, BigDecimal.ROUND_DOWN);
                doubleStr = String.valueOf(decimal);
                if (doubleStr.indexOf(".") + rounWay + 1 == rounWay || doubleStr.charAt(doubleStr.indexOf(".") + rounWay + 1) == '0') {
                    return decimal.doubleValue();
                } else {
                    return value;
                }
        }


    }


    /**
     * 见分进位(暂未使用)
     */
    public static boolean carry(Double f) {
        String a = String.valueOf(RoundTool.round(f, 2, BigDecimal.ROUND_HALF_DOWN));
        String b = a.substring(a.indexOf(".") + 2);
        if (Integer.valueOf(b) > 0) {
            return true;
        } else return false;
    }

    /**
     * 默认奇偶数:1 ; 向上取偶数:2 ; 向下取偶数:3 ; 向上取奇数:4 ; 向下取奇数:5 ;
     * peaccu 精度
     */
    public static boolean parity(BigDecimal bigDecimal, int peaccu) {
        //厘位
        if (NumberUtil.equals(peaccu, BaseDictEnum.PROD_PEACCU_3.getValue())) {
            String a = String.valueOf(RoundTool.round(bigDecimal, BaseDictEnum.PROD_PEACCU_3.getValue(), BigDecimal.ROUND_HALF_DOWN));
            String b = a.substring(a.indexOf(".") + 1);
            if (Integer.valueOf(b) % 2 == 0) {
                return true;
            } else return false;
        }
        //个人/企业精度：分位/小数点后两位
        else if (NumberUtil.equals(peaccu, BaseDictEnum.PROD_PEACCU_2.getValue())) {
            String a = String.valueOf(RoundTool.round(bigDecimal, BaseDictEnum.PROD_PEACCU_2.getValue(), BigDecimal.ROUND_HALF_DOWN));
            String b = a.substring(a.indexOf(".") + 1);
            if (Integer.valueOf(b) % 2 == 0) {
                return true;
            } else return false;
        }
        //个人/企业精度：角位/小数点后一位
        else if (NumberUtil.equals(peaccu, BaseDictEnum.PROD_PEACCU_1.getValue())) {
            String a = String.valueOf(RoundTool.round(bigDecimal, BaseDictEnum.PROD_PEACCU_1.getValue(), BigDecimal.ROUND_HALF_DOWN));
            String b = a.substring(a.indexOf(".") + 1);
            if (Integer.valueOf(b) % 2 == 0) {
                return true;
            } else return false;
        }
        //个人/企业精度：元位
        else if (NumberUtil.equals(peaccu, BaseDictEnum.PROD_PEACCU_4.getValue())) {
            String a = RoundTool.round(bigDecimal, 0, BigDecimal.ROUND_HALF_DOWN).toPlainString();
            if (Integer.valueOf(a) % 2 == 0) {
                return true;
            } else return false;
        }
        //个人/企业精度：十元位/小数点前两位
        else if (NumberUtil.equals(peaccu, BaseDictEnum.PROD_PEACCU_5.getValue())) {
            String a = RoundTool.round(bigDecimal, -1, BigDecimal.ROUND_HALF_DOWN).toPlainString();
            if (Integer.valueOf(a) % 2 == 0) {
                return true;
            } else return false;
        }
        //个人/企业精度：百元位/小数点前三位
        else if (NumberUtil.equals(peaccu, BaseDictEnum.PROD_PEACCU_6.getValue())) {
            String a = RoundTool.round(bigDecimal, -2, BigDecimal.ROUND_HALF_DOWN).toPlainString();
            if (Integer.valueOf(a) % 2 == 0) {
                return true;
            } else return false;
        } else {
            return false;
        }

    }


//    public static void main(String[] args) {
//
////         * 是否计算
////        private Boolean isPart = false;
////         * 比例
////        private BigDecimal persPart;
////         * 附加
////        private Double persAppe;
////         * 精度:  厘位：3 ; 分位：2 ; 角位：1 ; 元位：-10 ; 十元位：-20; 百位：-30
////        private Integer prec;
////         * 舍入原则 四舍五入：1 ; 截断舍去:2 ; 进位:3 ; 跨进位:4 ;
////        private Integer rounWay;
////         * 奇偶原则  默认奇偶数:1 ; 向上取偶数:2 ; 向下取偶数:3 ; 向上取奇数:4 ; 向下取奇数:5 ;
////        private Integer parity;
////         * 原始值
////        private Double protoValue;
////         * 上限
////        private Double upValue;
////         * 下限
////        private Double downValue;
//
//
//        PrecisionCounter pc1 = new PrecisionCounter();
//        pc1.setIsPart(true);//是否计算
//        pc1.setPersPart(new BigDecimal(0.005));//比例
//        pc1.setPersAppe(0D);//附加
//        pc1.setPrec(2);//精度
//        pc1.setRounWay(1);//舍入原则
//        pc1.setParity(1);//奇偶原则
//        pc1.setProtoValue(13659.25);//原始值
//        pc1.setUpValue(13659.25);//上限
//        pc1.setDownValue(1821.23);//下限
//
//        System.out.println("精确到厘位时：" + CalcEngine.calc(pc1));
//
//
//    }

}
